#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <pthread.h>
#include <sys/types.h>
#include <limits.h> 
#include <errno.h>
#include <unistd.h>

////////blok definicji////////////////
#define LOOPS 4
#define number_of_threads1 4
#define number_of_threads2 8

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; 
//pthread_mutex_t mutex_i = PTHREAD_MUTEX_INITIALIZER; 
//pthread_mutex_t mutex_j = PTHREAD_MUTEX_INITIALIZER; 


/////////zmienne globalne/////////////
unsigned int non_zeros;		//LICZBA NIEZEROWYCH ELEMENTÓW
unsigned int rowsPLUS1;		//ROZMIAR WEKTORA Z INDEKSAMI POCZĄTKÓW WIERSZY
unsigned int liczbaWierszy, liczbaKolumn, liczbaElementow =1;	//STAŁE OPISUJACE PARAMETRY MACIERZY
double * values;	//WSKAZNIK PRZECHOWUJACY WEKTOR WaRTOSCI MACIERZY
int *row_ptr;		//WSKAZNIK PRZECHOWUJACY INFORMACJE O WIERSZACH
int *col;			//WSKAzNIK PRZECHOWUJACY INFORMACJE O KOLUMNACH
WINDOW *my_win;
FILE *plik;
int a=1;
int postep1=0;
int postep2=0;		// zmienna globalna - licznik kolejnych działań, dodawań do wektora wynikowego
int pasek1=0;
int pasek2=0;
int r1,r2;
float procent1=0;
float procent2=0;
bool koniec=true;
int id_watku;
int start,stop;
int dlugosc1, dlugosc2;
int t1=0,t2=0;
int width, height;
WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);


/////////////////Struktura argumentu dla funkcji mnozenia////////////
typedef struct _Arg{
	unsigned int number_of_rows;
	unsigned int number_of_non_zeros;
	double *non_zeros;
	int *row_pointer;
	int *column_index;
	double *source_vector;
	double *destination_vector;
	int row_start;
	int row_stop;
	int number_of_threads;
	int pozycja;
	int *postep;
	int suma;
	int sleep;
}Argument;

///////////////funkcja mnozaca macierz przez wektor przystosowana do pracy z watkami
void *smvm_for(void *p)
{
	Argument* param= (Argument*)(p);

	for(param->pozycja=param->row_start;param->pozycja<=param->row_stop;param->pozycja++ ){
				//postep2=param->pozycja;
				//pthread_mutex_lock( &mutex_i );
				//
		
		id_watku=pthread_self();
				//printf("\n\rwatek: %d",postep2 );
    	start=param->row_start;
    	stop=param->row_stop;
   
    	
		
		
		for ( int j = param->row_pointer[param->pozycja]; j < param->row_pointer[param->pozycja+1]; j++ ){
		pthread_mutex_lock( &mutex1 );
			param->destination_vector[param->pozycja] += param->non_zeros[j]*param->source_vector[param->column_index[j]];
			//(*param->postep)++;
		 postep1++;
		  pthread_mutex_unlock( &mutex1 );  
        	}
        	//pthread_mutex_unlock( &mutex_i );
        	//
        	usleep(1500);
        	//sleep(1);
        	
	}
       	pthread_exit(NULL);
       return 0;
}

///////////////funkcja mnozaca macierz przez wektor przystosowana do pracy z watkami
void *smvm_for2(void *p)
{
	Argument* param= (Argument*)(p);

	for(param->pozycja=param->row_start;param->pozycja<=param->row_stop;param->pozycja++ ){
				//postep2=param->pozycja;
				//pthread_mutex_lock( &mutex_i );
	
		id_watku=pthread_self();
				//printf("\n\rwatek: %d",postep2 );
    	start=param->row_start;
    	stop=param->row_stop;

		
		
		for ( int j = param->row_pointer[param->pozycja]; j < param->row_pointer[param->pozycja+1]; j++ ){
		pthread_mutex_lock( &mutex2 );
			param->destination_vector[param->pozycja] += param->non_zeros[j]*param->source_vector[param->column_index[j]];
			//(*param->postep)++;
			postep2++;
		    pthread_mutex_unlock( &mutex2 );
       		}
        	//pthread_mutex_unlock( &mutex_i );
        	
        	usleep(1500);
        	
        	
	}
       	pthread_exit(NULL);
       return 0;
}

///////////standardowa funkcja mnozaca macierz w formacie CSR przez wektor///////////////
void smvm ( int *row_ptr, int* col_idx, double* value,double* x,double* y, int m) 
{  for ( int i=0; i <= m; i++ ){
        y[i] = 0;
        for ( int j = row_ptr[i]; j < row_ptr[i+1]; j++ ){
            y[i] += value[j]*x[col_idx[j]];
        }
    }
}


void* paski(void* _arg) {    
  	start_color();			/* Start color 			*/
	init_pair(1, COLOR_RED, COLOR_BLACK);
	init_pair(2, COLOR_GREEN, COLOR_BLACK);
bool koniec=false;
	
	//print_in_middle(stdscr, LINES / 2, 0, 0, "Viola !!! In color ...");
	

		while(!koniec)
		{
		
		if((procent2==100)&&(procent1==100)){koniec=true;}
		
		procent2=(float)(((float)postep2/(float)liczbaElementow)*100);
		 procent1=(float)(((float)postep1/(float)liczbaElementow)*100);
   			pasek2=(procent2*(width-7))/100;
   			pasek1=(procent1*(width-7))/100;
   			//move(17,4); attron(A_BOLD);
      			//wprintw(stdscr, "Watek 2:");  
      			attron(COLOR_PAIR(1));
			move(11,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   %0.2f procent",procent1); 
      			
      			move(13,pasek1+3); attron(A_BOLD);   //-100%
      			wprintw(stdscr, "|");
      			attroff(COLOR_PAIR(1));
      			attron(COLOR_PAIR(2));
      			move(18,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   %0.2f procent",procent2); 
   		
   			move(20,pasek2+3); attron(A_BOLD);   //-100%
								
      			wprintw(stdscr, "|");
      			attroff(COLOR_PAIR(2));
			refresh();
			//usleep(1000);
		}		
		

 return NULL;
}

void* watek2(void* _arg) {      

	
	sleep(1);
 return NULL;
}

/////////funkcja zerujaca wektor////////////
void init_wynik(double *wek, int liczbaKolumn)
{
		for(int k=0;k<liczbaKolumn;k++)
		{
			wek[k]=0;
		}
}

/////////funkcja ustawiajaca wektor//////////
void ustaw_wektor(double *vek, int count, double wartosc)
{
	for (unsigned int i = 0; i < count; i += 1)
	{
		vek[i]=wartosc;
	}
}


//////////funkcja wypisujaca wektor na ekran///

void wypiszVec( double * wek, int rozm)
{
	for (unsigned int i = 0; i < rozm; i ++)
	{
		printf("\n\rwektor: %lf" ,wek[i]);
	}
}

////////funkcja wczytujaca macierz w form CSR/////////

void get_matrix (char *strr)
{
	int s=0;
	int tmp,tmp2;
	
	//plik=fopen(str, "rb" );
	if  ((plik=fopen("raefskyCSR.txt", "r"))==NULL)
	{
 		printf("Nie moge otworzyc pliku!\n");   
	}
	fscanf(plik, "%d %d", &liczbaWierszy, &liczbaElementow);

	liczbaKolumn=liczbaWierszy;
	//printf("\n%d %d %d", liczbaWierszy,liczbaKolumn,liczbaElementow);

	col=new int[liczbaElementow];
	row_ptr=new int[liczbaWierszy+1];
	values=new double[liczbaElementow];


	while (feof(plik)==0 && s<liczbaElementow)
	{
		fscanf(plik, "%d %d %lf", &tmp,&tmp2,&values[s]);
		col[s]=tmp2-1;
		
		if (s<liczbaWierszy+1)
		{ 
			row_ptr[s]=tmp-1;
		}
	    s++;
	}
	if  (fclose(plik))
	{
		printf("Nie moge zamknac pliku!\n");
	}
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////MAIN////////////////////////////////////////////////
int main(){
	
	////deklaracja wektorów wynikowych////////////
	double * wynik_1, *wynik_2, *wynik_3, *wynik_4;
	
	////deklaracja wektora mnozonego////////
	double * vec;
  
	int y;
	int ch;

	int startx, starty;
	int i, j,r=0;
	char str[80];

	initscr();
	pthread_t id1;
	pthread_t id2;
    Argument *arg1;
    Argument *arg2;
 
      
             
		
           //  errno = pthread_create(&id2, NULL, watek2, NULL);     
    		//test_errno("Nie powiodło się pthread_create");     
		//test_errno("Nie powiodło się pthread_create");
	


	raw();
	keypad(stdscr, TRUE);           //We get F1, F2 etc..         
                            
        height = LINES;
        width = COLS;
        starty = 0; // Calculating for a center placement
        startx = 0;  
	printw("Press F1 to exit");
	
        refresh();
        my_win = create_newwin(height, width, starty, startx);
	move(2,4); attron(A_BOLD);
        wprintw(stdscr, "Mnozenie macierzy przez wektor"); 
	move(3,4); attron(A_BOLD);
        wprintw(stdscr, "pthreads/ncurses");  
	
	for (j=2;j<5;j++)
	for (i=COLS-25;i<COLS-22;i++){
	move(j,i); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "#");  }
	move(3,COLS-21); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "o");
	for (j=2;j<5;j++){
	move(j,COLS-19); attron(A_BOLD|A_BLINK);
        wprintw(stdscr, "#");  }

	my_win = create_newwin(height-7, width-2, 6, startx+1);

			move(8,4); attron(A_BOLD);
      			wprintw(stdscr, "Podaj nazwe macierzy:");  
 			getstr(str);
			move(8,45); attron(A_BOLD);
      			wprintw(stdscr, str);  
			move(10,4); attron(A_BOLD);
      			wprintw(stdscr, "Watek 1:");  
			move(11,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   0 procent"); 
			my_win = create_newwin(3, width-4, 12, startx+2);
				r=8;
			move(9+r,4); attron(A_BOLD);
      			wprintw(stdscr, "Watek 2:");  
			move(10+r,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   0 procent"); 
			my_win = create_newwin(3, width-4, 11+r, startx+2);

			/////////////Wczytanie macierzy//////////////
			get_matrix(str);
	
	
			/////////Utworzenei wektorów wynikowych///////////////
			vec=new double[liczbaKolumn];
			/////////////////////////////////////////////////////

			wynik_1=new double[liczbaKolumn];
			wynik_2=new double[liczbaKolumn];
				
			////////wypełnienie wekora mnozonego jedynkami/////
			ustaw_wektor(vec,liczbaKolumn,1);	
				
				
				arg1=new Argument[number_of_threads1];
				pthread_t *threads1= new pthread_t[number_of_threads1];
				
				arg2=new Argument[number_of_threads2];
				pthread_t *threads2= new pthread_t[number_of_threads2];
				
				dlugosc1 = (int)(((float)liczbaWierszy/(float)number_of_threads1));
				dlugosc2 = (int)(((float)liczbaWierszy/(float)number_of_threads2));
				
							//smvm(row_ptr, col, val, vec, wynik_std, liczbaKolumn-1);
							//smvm(row_ptr, col, val, vec, wynik_watek, liczbaKolumn);
				pthread_create(&id1, NULL, paski, NULL);
			
				///////Ustawienie struktury argumentu//////////////////
				 arg1[0].number_of_rows=liczbaWierszy;
				 arg1[0].number_of_non_zeros=liczbaElementow;
				 arg1[0].non_zeros=values;
				 arg1[0].row_pointer=row_ptr;
				 arg1[0].column_index=col;
				 arg1[0].source_vector=vec;
				 arg1[0].destination_vector=wynik_1;
				 arg1[0].row_start=0;
				 arg1[0].row_stop=dlugosc1;
				 arg1[0].pozycja=0;
				 arg1[0].postep=&postep1;
				 arg1[0].sleep=5000;
				 
				 ///////Ustawienie drugiej struktury argumentu//////////////////
				 arg2[0].number_of_rows=liczbaWierszy;
				 arg2[0].number_of_non_zeros=liczbaElementow;
				 arg2[0].non_zeros=values;
				 arg2[0].row_pointer=row_ptr;
				 arg2[0].column_index=col;
				 arg2[0].source_vector=vec;
				 arg2[0].destination_vector=wynik_2;
				 arg2[0].row_start=0;
				 arg2[0].row_stop=dlugosc2;
				 arg2[0].pozycja=0;
				 arg2[0].postep=&postep2;
				 arg2[0].sleep=300;
				
				////////Wywołanie wątków z odpowiednimiparametrami ustawianymi w arg/////////
				//for(int i=0;i<number_of_threads1; i++){
					
					arg1[1]=arg1[0];
					arg1[2]=arg1[0];
					arg1[3]=arg1[0];
					
					arg1[0].row_start=dlugosc1*0;
					arg1[1].row_start=dlugosc1*1;
					arg1[2].row_start=dlugosc1*2;
					arg1[3].row_start=dlugosc1*3;
					
					arg1[0].row_stop=dlugosc1*1-1;
					arg1[1].row_stop=dlugosc1*2-1;;
					arg1[2].row_stop=dlugosc1*3-1;
					arg1[3].row_stop=liczbaWierszy;
					
					
							//arg[i].row_stop=dlugosc*(i+1);
					

			 		//printf( "\n\rwatek: %d, row_start: %d, row_stop: %d,dlug: %d, (dlug*n_thr): %d",i,arg1[0].row_start,arg1[0].row_stop,arg1[0].row_stop-arg1[0].row_start+1,dlugosc1*number_of_threads1); 
					//printf( "\n\rwatek: %d, row_start: %d, row_stop: %d,dlug: %d, (dlug*n_thr): %d",i,arg1[1].row_start,arg1[1].row_stop,arg1[1].row_stop-arg1[1].row_start+1,dlugosc1*number_of_threads1); 
					//printf( "\n\rwatek: %d, row_start: %d, row_stop: %d,dlug: %d, (dlug*n_thr): %d",i,arg1[2].row_start,arg1[2].row_stop,arg1[2].row_stop-arg1[2].row_start+1,dlugosc1*number_of_threads1); 
					//printf( "\n\rwatek: %d, row_start: %d, row_stop: %d,dlug: %d, (dlug*n_thr): %d",i,arg1[3].row_start,arg1[3].row_stop,arg1[3].row_stop-arg1[3].row_start+1,dlugosc1*number_of_threads1); 
				
				
					arg2[1]=arg2[0];
					arg2[2]=arg2[0];
					arg2[3]=arg2[0];
					arg2[4]=arg2[0];
					arg2[5]=arg2[0];
					arg2[6]=arg2[0];
					arg2[7]=arg2[0];
					
					arg2[0].row_start=dlugosc2*0;
					arg2[1].row_start=dlugosc2*1;
					arg2[2].row_start=dlugosc2*2;
					arg2[3].row_start=dlugosc2*3;
					arg2[4].row_start=dlugosc2*4;
					arg2[5].row_start=dlugosc2*5;
					arg2[6].row_start=dlugosc2*6;
					arg2[7].row_start=dlugosc2*7;
					
					arg2[0].row_stop=dlugosc2*1-1;
					arg2[1].row_stop=dlugosc2*2-1;
					arg2[2].row_stop=dlugosc2*3-1;
					arg2[3].row_stop=dlugosc2*4-1;
					arg2[4].row_stop=dlugosc2*5-1;
					arg2[5].row_stop=dlugosc2*6-1;
					arg2[6].row_stop=dlugosc2*7-1;
					arg2[7].row_stop=liczbaWierszy;
					
					pthread_create(&threads1[0], NULL, smvm_for, (void*)&arg1[0]);
					pthread_create(&threads1[1], NULL, smvm_for, (void*)&arg1[1]);
					pthread_create(&threads1[2], NULL, smvm_for, (void*)&arg1[2]);
					pthread_create(&threads1[3], NULL, smvm_for, (void*)&arg1[3]);
					
					
					pthread_create(&threads1[0], NULL, smvm_for2, (void*)&arg2[0]);
					pthread_create(&threads1[1], NULL, smvm_for2, (void*)&arg2[1]);
					pthread_create(&threads1[2], NULL, smvm_for2, (void*)&arg2[2]);
					pthread_create(&threads1[3], NULL, smvm_for2, (void*)&arg2[3]);
					pthread_create(&threads1[4], NULL, smvm_for2, (void*)&arg2[4]);
					pthread_create(&threads1[5], NULL, smvm_for2, (void*)&arg2[5]);
					pthread_create(&threads1[6], NULL, smvm_for2, (void*)&arg2[6]);
					pthread_create(&threads1[7], NULL, smvm_for2, (void*)&arg2[7]);
					
				
				
				 	
pthread_join(*threads1, NULL);
pthread_join(*threads2, NULL);
//koniec=false;
pthread_join(id1, NULL); 

	//for (unsigned int i = 0; i < liczbaKolumn; i ++)
	//{
	//	printf("\n\r %d wektor1: %lf,   wektor2: %lf" ,i,wynik_1[i], wynik_2[i]);
	//}
//pthread_join(id1, NULL);move(18,4); attron(A_BOLD);wprintw(stdscr, "Postep:   %d procent",postep2); 

/*

	
				
			while(procent2<=100){
			//move(10+r,4); attron(A_BOLD);
  	  		//printf( "\n\rwatek_pid: %d , i: %d, row_start: %d, row_stop: %d,dlug: %d",id_watku,postep2,start,stop,dlugosc); 
   			//printf( "\n\rSuma1: %d, Suma2: %d",arg[0].suma, arg[1].suma);
   			procent2=(float)arg[0].suma/(float)dlugosc*100;
   			pasek2=(procent2*(width-4))/100;
   			//move(17,4); attron(A_BOLD);
      			//wprintw(stdscr, "Watek 2:");  
			move(18,4); attron(A_BOLD);
      			wprintw(stdscr, "Postep:   %0.2f procent",procent2); 
   			
   			
   			
			move(20,pasek2+3); attron(A_BOLD);   //-100%
								6
      			wprintw(stdscr, "|");
			refresh();
			//usleep(100);			
			}
			//	22
				for (int i=0;i<liczbaKolumn;i++){
				if(wynik_std[i]!=wynik_watek[i])
				printf("\n\rBlad, %f.9 %f.9",wynik_std[i],wynik_watek[i] );
				printf("\n\ri: %d, wynik watek: %f wyniki std: %f",i ,wynik_watek[i],wynik_std[i]);
				}
				printf("Sukces");
				
				*/
			

        getch();     
	endwin();
	//errno = pthread_join(id1, NULL); 
             
//errno = pthread_join(id2, NULL);

return 0;
};


WINDOW *create_newwin(int height, int width, int starty, int startx)
{       WINDOW *local_win;

        local_win = newwin(height, width, starty, startx);
        box(local_win, 0 , 0);        
        
 
        wrefresh(local_win);           
        return local_win;
}

